package com.zenchn.controller;

import com.zenchn.annotation.OperLog;
import com.zenchn.entity.*;
import com.zenchn.enums.OperLogActionEnum;
import com.zenchn.enums.SessionAttributeEnum;
import com.zenchn.exception.CheckException;
import com.zenchn.model.*;
import com.zenchn.service.*;
import com.zenchn.util.WxUtil;
import com.zenchn.utils.CommonUtils;
import com.zenchn.utils.PwdHashSaltUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;


/**
 * 异步登录相关控制层
 *
 * @author panbingqi
 * @date 2021-06-01
 */
@RestController
@RequestMapping(value = "login")
@Api(value = "管理平台登录登出模块", tags = "登录登出")
public class LoginController {

    private final String MODULE = "登录登出";

    @Resource
    private SysFileService sysFileService;

    @Resource
    private SysConfig sysConfig;

    @Resource
    private RoleService roleService;

    @Resource
    private AccountRoleMapperService accountRoleMapperService;

    @Resource
    private RoleAuthorityMapperService roleAuthorityMapperService;

    @Resource
    private AccountExtendService accountExtendService;

    @Value("${custom.wx-app-id}")
    private String wxAppId;

    @Value("${custom.wx-app-secret}")
    private String wxAppSecret;

    /**
     * 用户 登录操作
     *
     * @param account
     * @param request
     * @param response
     * @return
     */
    @OperLog(module = MODULE, action = OperLogActionEnum.LOGIN, detail = "管理平台用户登录操作")
    @ApiOperation(value = "登录接口", notes = "用户登录操作")
    @ApiParam(type = "LoginAccountDO", name = "account", value = "登录账号密码对象", required = true)
    @PostMapping(value = "/login")
    public ResponseEntity<MyHttpLoginResult> login(
            @RequestBody @Valid LoginAccountDO account, BindingResult results,
            HttpServletRequest request, HttpServletResponse response) {
        MyHttpLoginResult result = new MyHttpLoginResult(request.getRequestURI());
        try {

            Subject subject = SecurityUtils.getSubject();
            // 身份验证
            subject.login(new UsernamePasswordToken(account.getAccount(), account.getPassword(), account.getRememberMe()));
            final TSysAccount currAccount = (TSysAccount) subject.getPrincipal();

            //设置用户基本信息
            request.getSession().setAttribute(SessionAttributeEnum.CURRENT_ACCOUNT_INFO.getKey(), currAccount);
            //设置用户扩展信息
            TMiAccountExtend accountExtend = accountExtendService.selectByPrimaryKey(currAccount.getAccountId());
            request.getSession().setAttribute(SessionAttributeEnum.CURRENT_ACCOUNT_EXTEND_INFO.getKey(), accountExtend);

            result.setToken(subject.getSession().getId());

            TSysAccountRoleMapperExample accountRoleMapperExample = new TSysAccountRoleMapperExample();
            accountRoleMapperExample.createCriteria().andAccountIdEqualTo(currAccount.getAccountId());
            List<TSysAccountRoleMapper> accountRoleMapperList = accountRoleMapperService.selectByExample(accountRoleMapperExample);
            if (null != accountRoleMapperList) {
                List<String> roleIds = accountRoleMapperList.stream().map(TSysAccountRoleMapper::getRoleId).collect(Collectors.toList());

                final List<TSysAuthority> authorityList = roleAuthorityMapperService.selectAuthorityByRoleIds(roleIds);
                // 添加权限
                if (null != authorityList) {
                    result.setAuthList(authorityList.stream().map(TSysAuthority::getAuthSign).collect(Collectors.toSet()));
                }

                TSysRoleExample roleExample = new TSysRoleExample();
                roleExample.createCriteria().andRoleIdIn(roleIds);
                final List<TSysRole> roleList = roleService.selectByExample(roleExample);
                // 添加角色
                if (null != roleList) {
                    result.setRoleList(roleList.stream().map(TSysRole::getRoleSign).collect(Collectors.toSet()));
                }
            }
            //是否强制更改密码
            String newPwd;
            if (null != currAccount.getTel()) {
                newPwd = currAccount.getAccount() + "_" + currAccount.getTel().substring(currAccount.getTel().length() - 4);
            } else {
                newPwd = currAccount.getAccount() + "_" + CommonUtils.randomNumber(4);
            }
            if (null == currAccount.getFinalLoginIp() && null == currAccount.getFinalLoginLocation() && null == currAccount.getFinalLoginTime()) {
                result.setIsForceChangePwd(true);
            } else if (currAccount.getPassword().equals(PwdHashSaltUtils.primitiveSha256Hex(currAccount.getAccount(), newPwd))) {
                result.setIsForceChangePwd(true);
            } else {
                result.setIsForceChangePwd(false);
            }

            //登录人头像
            if (null != currAccount.getPortraitFileId() && !"".equals(currAccount.getPortraitFileId())) {
                TSysFile sf = sysFileService.selectByPrimaryKey(currAccount.getPortraitFileId());
                if (null != sf) {
                    String portraitUrl = sysConfig.getCurrentUseFormalFileUrl() + "/" + sf.getFilePath();
                    request.getSession().setAttribute(SessionAttributeEnum.PORTRAIT_URL.getKey(), portraitUrl);
                }
            }

            result.setMessage("登录成功!");
            result.setStatus(HttpStatus.OK.value());
            return new ResponseEntity<MyHttpLoginResult>(result, HttpStatus.OK);

        } catch (LockedAccountException e) {
            result.setMessage(e.getMessage());
            result.setStatus(HttpStatus.FORBIDDEN.value());
            result.setError(HttpStatus.FORBIDDEN.getReasonPhrase());
            return new ResponseEntity<MyHttpLoginResult>(result, HttpStatus.FORBIDDEN);
        } catch (AuthenticationException e) {
            result.setMessage(e.getMessage());
            result.setStatus(HttpStatus.BAD_REQUEST.value());
            result.setError(HttpStatus.BAD_REQUEST.getReasonPhrase());
            return new ResponseEntity<MyHttpLoginResult>(result, HttpStatus.BAD_REQUEST);
        } catch (Exception e) {
            e.printStackTrace();
            result.setMessage(e.getMessage());
            result.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            result.setError(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
            return new ResponseEntity<MyHttpLoginResult>(result, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }


    /**
     * 用户 登录操作
     *
     * @param account
     * @param request
     * @param response
     * @return
     */
    @OperLog(module = MODULE, action = OperLogActionEnum.LOGIN, detail = "微信用户登录操作")
    @ApiOperation(value = "微信用户登录操作", notes = "微信用户登录操作")
    @ApiParam(type = "LoginAccountDO", name = "account", value = "登录账号密码对象", required = true)
    @PostMapping(value = "/wxLogin")
    public ResponseEntity<MyHttpLoginResult> Wxlogin(
            @RequestBody @Valid WxLoginAccountDO account, BindingResult results,
            HttpServletRequest request, HttpServletResponse response) {
        MyHttpLoginResult result = new MyHttpLoginResult(request.getRequestURI());
        try {

            Subject subject = SecurityUtils.getSubject();
            account.setPassword(CommonUtils.sha256Hex(account.getPassword()));
            // 身份验证
            subject.login(new UsernamePasswordToken(account.getAccount(), account.getPassword(), false));
            final TSysAccount currAccount = (TSysAccount) subject.getPrincipal();

            //设置用户基本信息
            request.getSession().setAttribute(SessionAttributeEnum.CURRENT_ACCOUNT_INFO.getKey(), currAccount);

            //获取用户唯一标识 OpenID
            String code = account.getCode();
            String openid = WxUtil.getOpenId(wxAppId, wxAppSecret, code);
            if (null == openid) {
                throw new CheckException("获取OpenId失败");
            }
            request.getSession().setAttribute(SessionAttributeEnum.CURRENT_ACCOUNT_OPEN_ID.getKey(), openid);

            //设置用户扩展信息
            TMiAccountExtend accountExtend = accountExtendService.selectByPrimaryKey(currAccount.getAccountId());
            //存储openID
            accountExtend.setWxOpenId(openid);
            accountExtendService.updateByPrimaryKey(accountExtend);
            request.getSession().setAttribute(SessionAttributeEnum.CURRENT_ACCOUNT_EXTEND_INFO.getKey(), accountExtend);
            result.setToken(subject.getSession().getId());

            //是否强制更改密码
            String newPwd;
            if (null != currAccount.getTel()) {
                newPwd = currAccount.getAccount() + "_" + currAccount.getTel().substring(currAccount.getTel().length() - 4);
            } else {
                newPwd = currAccount.getAccount() + "_" + CommonUtils.randomNumber(4);
            }
            if (null == currAccount.getFinalLoginIp() && null == currAccount.getFinalLoginLocation() && null == currAccount.getFinalLoginTime()) {
                result.setIsForceChangePwd(true);
            } else if (currAccount.getPassword().equals(PwdHashSaltUtils.primitiveSha256Hex(currAccount.getAccount(), newPwd))) {
                result.setIsForceChangePwd(true);
            } else {
                result.setIsForceChangePwd(false);
            }

            //登录人头像
            if (null != currAccount.getPortraitFileId() && !"".equals(currAccount.getPortraitFileId())) {
                TSysFile sf = sysFileService.selectByPrimaryKey(currAccount.getPortraitFileId());
                if (null != sf) {
                    String portraitUrl = sysConfig.getCurrentUseFormalFileUrl() + "/" + sf.getFilePath();
                    request.getSession().setAttribute(SessionAttributeEnum.PORTRAIT_URL.getKey(), portraitUrl);
                }
            }

            result.setAccountExtend(accountExtend);
            result.setMessage("登录成功!");
            result.setStatus(HttpStatus.OK.value());
            return new ResponseEntity<MyHttpLoginResult>(result, HttpStatus.OK);

        } catch (LockedAccountException e) {
            result.setMessage(e.getMessage());
            result.setStatus(HttpStatus.FORBIDDEN.value());
            result.setError(HttpStatus.FORBIDDEN.getReasonPhrase());
            return new ResponseEntity<MyHttpLoginResult>(result, HttpStatus.FORBIDDEN);
        } catch (AuthenticationException e) {
            result.setMessage(e.getMessage());
            result.setStatus(HttpStatus.BAD_REQUEST.value());
            result.setError(HttpStatus.BAD_REQUEST.getReasonPhrase());
            return new ResponseEntity<MyHttpLoginResult>(result, HttpStatus.BAD_REQUEST);
        } catch (Exception e) {
            e.printStackTrace();
            result.setMessage(e.getMessage());
            result.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            result.setError(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
            return new ResponseEntity<MyHttpLoginResult>(result, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 用户 登出操作
     *
     * @param request
     * @param response
     * @return
     */
    @ApiOperation(value = "用户登出接口", notes = "用户退出登录操作")
    @GetMapping(value = "/logout")
    public ResponseEntity<HttpResult> logout(HttpServletRequest request, HttpServletResponse response) {
        HttpResult result = new HttpResult(request.getRequestURI());
        try {
            Subject subject = SecurityUtils.getSubject();
            subject.logout();
            result.setMessage("退出登录成功.");
            result.setStatus(HttpStatus.OK.value());
            return new ResponseEntity<HttpResult>(result, HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
            result.setMessage(e.getMessage());
            result.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            result.setError(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
            return new ResponseEntity<HttpResult>(result, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 微信用户登出接口
     *
     * @param request
     * @param response
     * @return
     */
    @ApiOperation(value = "微信用户登出接口", notes = "微信用户登出接口")
    @GetMapping(value = "/wxLogout")
    public ResponseEntity<HttpResult> wxLogout(HttpServletRequest request, HttpServletResponse response) {
        HttpResult result = new HttpResult(request.getRequestURI());
        try {
            Subject subject = SecurityUtils.getSubject();
            subject.logout();
            result.setMessage("退出登录成功.");
            result.setStatus(HttpStatus.OK.value());
            return new ResponseEntity<HttpResult>(result, HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
            result.setMessage(e.getMessage());
            result.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            result.setError(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
            return new ResponseEntity<HttpResult>(result, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}
